"""
PySCeS - Core2
Author: Brett G. Olivier (brettolivier@gmail.com)
Copyright (C) Brett. G. Olivier, Stellenbosch, 2007.
The source code contained in this file is not licensed for use
or redistribution without the author's prior and express permission.
"""

__version__ = "0.1"

import os, copy, time
from getpass import getuser

CurrentDirectory = os.getcwd() # temporary thing

from InfixParser import MyInfixParser
InfixParser = MyInfixParser()
InfixParser.buildlexer()
InfixParser.buildparser(debug=0, debugfile='infix.dbg', tabmodule='infix_tabmodule')
InfixParser.setNameStr('self.', '()')


class CoreToPsc(object):
    core = None
    name = ''
    header_block = ''
    fixed_block = ''
    reaction_block = ''
    species_block = ''
    parameter_block = ''
    arule_block = ''
    notes_block = ''
    SPECIES_CURRENT_VALUE = False
    FIXED_SPECIES_CURRENT_VALUE = False
    PARAMETER_CURRENT_VALUE = False
    
    # future
    compartment_block = ''

    def __init__(self, core):
        self.core = core
        self.name = self.core.name.replace('.psc','')
        self.name = self.core.name.replace('.xml','')

    def setHeader(self, s=''):
        out = '# This PSC file generated by pysXgen (%s)\n# \n# ' % time.strftime("20%y-%m-%y %H:%M")
        ccount = 0
        for c in range(len(s)):
            ccount += 1
            if ccount >= 60 and ccount <=75 and s[c] in [' ', '\t', '\n', '-']:
                out += s[c]+'\n# '
                ccount = 0
            else:
                out += s[c]
        out += '\n# \n# Model: %s\n' % self.core.getName()
        out += ' \n'
        self.header_block = out

    def setFixed(self):
        out = 'FIX: '
        for f in self.core.species:
            if f.fixed:
                out += '%s ' % f.name
        out += '\n \n'
        self.fixed_block = out
        
    def setReactions(self):
        out = ''
        for r in self.core.reactions:
            rout = '\n%s:\n' % r.name
            cnt = 0
            for lh in r.hasSubstrates():
                if cnt != 0:
                    rout += ' + '
                if abs(r.stoichiometry[lh]) == 1.0:
                    rout += '%s' % lh
                else:
                    rout += '{%s}%s' % (abs(r.stoichiometry[lh]), lh)
                cnt += 1
            if r.reversible:
                rout += ' = '
            else:
                rout += ' > '
            cnt = 0
            for rh in r.hasProducts():
                if cnt != 0:
                    rout += ' + '
                if abs(r.stoichiometry[rh]) == 1.0:
                    rout += '%s' % rh
                else:
                    rout += '{%s}%s' % (abs(r.stoichiometry[rh]), rh)
                cnt += 1
            rout += '\n%s\n' % r.formula
            if len(r.hasModifiers()) > 0:
                rout += '# %s has modifier(s): ' % r.name
                for m in r.hasModifiers():
                    rout += '%s ' % m
                rout += ' \n'
            out += rout
        out += ' \n'
        self.reaction_block = out

    def setSpecies(self):
        out = ''
        fixed = ''
        var = ''
        for f in self.core.species:
            if not f.fixed:
                if not self.SPECIES_CURRENT_VALUE:
                    value = f.value_initial
                else:
                    value = f()
                var += '%s = %s\n' % (f.name, value)
            else:
                if not self.FIXED_SPECIES_CURRENT_VALUE:
                    value = f.value_initial
                else:
                    value = f()
                fixed += '%s = %s\n' % (f.name, value)
        out += '# Fixed species\n'
        out += fixed
        out += ' \n'
        out += '# Variable species\n'
        out += var
        out += ' \n'
        self.species_block = out

#TODO:
    def setParameters(self):
        out = ''
        processed = []
        def setP(p2, out2, proced):
            if not self.PARAMETER_CURRENT_VALUE:
                if p2.value_initial != None:
                    value = p2.value_initial
                else:
                    value = p2()
            else:
                value = p2()    
            if p2.name in proced:
                out2 += '# '
            out2 += '%s = %s' % (p2.name, value)
            if not hasattr(p2, 'code_string'):
                if p2.name in proced:
                    out2 += '\t# (initialised)'
                out2 += '\n'
            else:
                if p2.name in proced:
                    out2 += '\t# (initialised)\n'
                else:
                    out2 += '\t# (rule)\n'
            return out2
                
        for r in self.core.reactions:
            out += '# %s parameters\n' % r.name
            for p in r.parameters:
                out = setP(p, out, processed)
                processed.append(p.name)
            out += '\n'
        if len(processed) != len(self.core.hasGlobalParameters()):
            out += '# Additional (global) parameters\n'
            for p in self.core.hasGlobalParameters():
                if p not in processed:
                    p = self.core.get(p)
                    out = setP(p, out, processed)
                    processed.append(p.name)
            out += ' \n'
        self.parameter_block = out

    def setAssignmentRules(self):
        out = ''
        cnt = 0
        for p in self.core.global_parameters:
            if hasattr(p, 'code_string'):
                if cnt == 0:
                    out += '# Assignment rules\n'
                    cnt += 1
                formula = p.code_string.split('=')[1]
                formula = formula.replace('()','')
                formula = formula.replace('numpy.','')
                out += '!F self.%s = %s\n' % (p.name, formula)
        out += ' \n'
        self.arule_block = out

    def setNotes(self, s):
        out = '# Notes\n# '
        ccount = 0
        for c in range(len(s)):
            ccount += 1
            if ccount >= 60 and ccount <=75 and s[c] in [' ', '\t', '\n', '-']:
                out += s[c]+'\n# '
                ccount = 0
            else:
                out += s[c]
        out += ' \n'
        self.notes_block = out

    def write(self, filename=None, directory=None, getF=False):
        if filename == None:
            filename = self.name+'.psc'
        if directory != None:
            assert os.path.exists(directory), '\n%s does not exist.' % directory
            filename = os.path.join(directory, filename)
        print 'Writing file: %s' % filename
        outF = file(filename, 'w')
        outF.write(self.header_block)
        outF.write(self.fixed_block)
        outF.write(self.reaction_block)
        outF.write(self.arule_block)        
        outF.write(self.species_block)
        outF.write(self.parameter_block)
        outF.write(self.notes_block)
        if not getF:
            outF.flush()
            outF.close()
        else:
            return outF
        

class PscToCore(object):
    ModelDir = None
    WorkDir = None

    # old
    ModelFile = None    
    _PysMod__InitStrings = None
    InitParams = None #suspect
    fixed_species = None
    species = None
    parameters = None
    reactions = None
    modifiers = None
    __function_forced_str__ = None
    _Function_time = None
    _Function_user = None
    _Function_init = None
    _PysMod__NetworkDict = None
    
    def __init__(self):
        try:
            from pysces.PyscesParse import PySCeSParser
            self.pscParser = PySCeSParser(debug=0)
        except:
            print "\nYou need PySCeS installed to use this moudle"

    def getPSCFileFromDisk(self, ModelFile, ModelDir=None, WorkDir=None):
        """
        find and set up an existing psc file for parsing
        """
        
        if ModelFile[-4:] != '.psc':
            print "Assuming .psc extension"
            ModelFile += '.psc'
        if ModelDir == None:
            # this will probably change in future - bgoli
            ModelDir = CurrentDirectory
        if WorkDir == None:
            WorkDir = CurrentDirectory            
        assert os.path.exists(os.path.join(ModelDir, ModelFile)), \
            '\nFile %s does not exist' % os.path.join(ModelDir, ModelFile)
        setattr(self, 'ModelFile', ModelFile)
        setattr(self, 'ModelDir', ModelDir)
        assert os.path.exists(WorkDir), \
            '\nDirectory %s does not exist' % WorkDir
        setattr(self, 'WorkDir', WorkDir)
    
    def getPSCFileFromString(self, ModelString, WorkDir=None):
        """
        set up a temporary psc file for parsing from model string
        """
        if WorkDir == None:
            WorkDir = CurrentDirectory            
        assert os.path.exists(WorkDir), \
            '\nDirectory %s does not exist' % WorkDir
        setattr(self, 'WorkDir', WorkDir)
        setattr(self, 'ModelDir', WorkDir)
        ModelFile = '%s.psc' % time.time()
        Mfile = file(os.path.join(WorkDir,ModelFile),'w')
        Mfile.write(ModelString)
        Mfile.close()
        setattr(self, 'ModelFile', ModelFile)
        
    
    def getParsedModel(self):
        """
        Parse the input file associated with the PySCeS model instance and assign the basic model attributes
        """        

        self.pscParser.ParsePSC(self.ModelFile,self.ModelDir,self.WorkDir)

        # from _PysMod__NetworkDict
        self._PysMod__InitStrings = [s.replace('self.','') for s in self.pscParser.InitStrings]
        for s in self.pscParser.InitStrings:
            exec(s)
        self.InitParams = [s.replace('self.','') for s in self.pscParser.InitParams]
        self.fixed_species = copy.copy(self.pscParser.fixed_species)
        self.species = copy.copy(self.pscParser.species)
        self.parameters = copy.copy(self.pscParser.parameters)
        self.reactions = copy.copy(self.pscParser.reactions)
        self.modifiers = copy.copy(self.pscParser.modifiers)
        #print self.modifiers
        self._Function_forced = self.pscParser.ForceFunc.replace('self.','')
        self._Function_forced = self.pscParser.ForceFunc.replace('scipy.','')
        self._Function_time = self.pscParser.TimeFunc.replace('self.','')
        self._Function_user = self.pscParser.UserFunc.replace('self.','')
        self._Function_init = self.pscParser.InitFunc.replace('self.','')
        self._PysMod__NetworkDict = copy.copy(self.pscParser.NetworkDict)
        # new

        
    def getModel(self):
        return self

class CoreToSBML(object):
    core = None
    name = None
    SBML = None
    level = 2
    model = None
    document = None

    def __init__(self, core):
        self.core = core
        self.name = self.core.name.replace('.psc','')
        self.name = self.core.name.replace('.xml','')
        if os.sys.platform == 'win32':
            try:
                import pysces.libsbml.libsbmlinit as SBML
                self.SBML = SBML
            except Exception, e:
                print e
                print '\nWindows sbml load error'
                self.SBML = None
        else:
            try:
                import libsbml as SBML
                self.SBML = SBML
            except Exception, e:
                print e
                print 'Posix sbml load error'
                self.SBML = None    

    def createModel(self):
        self.model = self.SBML.Model()
        self.model.setName(self.name)
        self.document = self.SBML.SBMLDocument()
        ## self.document.setLevel(self.level) # libsbml3

    def setCompartments(self):
        for cs in self.core.compartments:
            comp_def = self.model.createCompartment()
            comp_def.setId(cs[0])
            comp_def.setName(cs[0])
            comp_def.setVolume(float(cs[1]))

    def setDescription(self, txt=None):
        if txt == None:
            txt = self.core.getDescription()
        else:
            self.core.setDescription(txt)            
        notes = '<html xhtml="http://www.w3.org/1999/xhtml">'
        notes = '<head><title></title></head>'
        notes += '<span style="font-family: Courier New,Courier,monospace;">'
        notes += txt
        notes += '</span>'
        notes += '</body></html>'
        # for libsbml3 ... not working
        xmln = self.SBML.XMLNode()
        xmln.append(notes)
        self.model.setNotes(xmln)

    def setUnits(self, **kwargs):
        #print kwargs
        if len(kwargs.keys()) > 0:
            self.core.setGlobalUnits(**kwargs)
        ud = self.core.getGlobalUnits()
        for un in ud.keys():
            vdef = self.model.createUnitDefinition()
            if un == 'litre': id = 'volume'
            if un == 'mole': id = 'substance'
            if un == 'second': id = 'time'
            vdef.setId(id)
            vdef.setName(id)
            vu = self.model.createUnit()
            vu.setKind(self.SBML.UnitKind_forName(un))
            vu.setMultiplier(ud[un][0])
            vu.setScale(ud[un][1])
            vu.setOffset(0)

    def setSpecies(self):
        for spe in self.core.species:
            s = self.model.createSpecies()
            s.setId(spe.name)
            s.setName(spe.name)
            if spe.compartment not in [p[0] for p in self.core.compartments]:
                print 'warning: defining 1 volume compartment \"%s\"' % spe.compartment
                self.setCompartment(spe.compartment, 1.0)
            s.setCompartment(spe.compartment)            
            if spe.name in self.core.hasFixedSpecies():
                s.setBoundaryCondition(True)
                s.setConstant(True)
            else:
                s.setBoundaryCondition(False)
            s.setInitialConcentration(spe())

    def setParameters(self):
        for par in self.core.global_parameters:
            #print 'Adding parameter:', par.name
            #print type(par)
            
            p = self.model.createParameter()
            p.setId(par.name)
            p.setName(par.name)
            p.setValue(par())
            # first attempt, check for a formula ... could be done with type instead
            if hasattr(par, 'formula'):
                p.setConstant(False)
                r = self.model.createAssignmentRule()
                r.setVariable(par.name)
                r.setFormula(par.formula)


    def setReactions(self):
        for rxn in self.core.reactions:
            print 'Adding reaction:', rxn.name
            r = self.model.createReaction()
            r.setId(rxn.name)
            r.setName(rxn.name)
            for s in rxn.substrates:
                print '\t' + rxn.name +' has substrate: ' + s.name + ' (%s)' % abs(rxn.stoichiometry[s.name])
                r.addReactant(self.SBML.SpeciesReference(s.name,\
                                abs(rxn.stoichiometry[s.name])))
            for p in rxn.products:
                print '\t' + rxn.name +' has product: ' + p.name + ' (%s)' % abs(rxn.stoichiometry[p.name])
                r.addProduct(self.SBML.SpeciesReference(p.name,\
                                abs(rxn.stoichiometry[p.name])))                
            for m in rxn.modifiers:
                print '\t' + rxn.name +' has modifier: ' + m.name
                self.model.createModifier().setSpecies(m.name)

            formula = rxn.formula.replace('scipy.','')
            if self.core.compartments[0][0] not in self.core.hasGlobalParameters():
                formula = self.core.compartments[0][0] + ' * ( '+ formula +' )'
            r.setKineticLaw(self.SBML.KineticLaw(formula))

            if rxn.reversible:
                r.setReversible(True)
            else:
                r.setReversible(False)

    def getSBMLmodel(self):
        return self.model

    def getSBMLdocument(self):
        self.document.setModel(self.model)
        return self.document
    
    def getSBML(self):
        return self.getSBMLdocument().toSBML()

    def writeSBML2file(self, name, path=None):
        name += '.xml'
        if path == None:
            name = os.path.join(os.getcwd(), name)
        else:
            assert os.path.exists(path), "\n\"%s\" is not a valid path" % path
            name = os.path.join(path, name)
        try: UseR = getuser()
        except: UseR = ''            
        h1 = '<!-- Created with PySCeS ('+ __version__ + ') on ' + time.strftime("%a, %d %b %Y %H:%M:%S") + ' by '+UseR+' -->\n'
        F = file(name, 'w')
        F.write(h1 + self.getSBML())
        F.close()
        print 'Model %s exported as: %s' % (self.name, name)


class SbmlToCore(object):
    SBML = None
    level = 2
    sbml_string = None
    sbml_file = None
    model = None
    document = None
    core = None
    # old
    ModelFile = None
    _PysMod__InitStrings = None
    InitParams = None
    fixed_species = None
    species = None
    parameters = None
    reactions = None
    modifiers = None
    __function_forced_str__ = None
    _Function_time = None
    _Function_user = None
    _Function_init = None
    _PysMod__NetworkDict = None

    def __init__(self):
        if os.sys.platform == 'win32':
            try:
                import pysces.libsbml.libsbmlinit as SBML
                self.SBML = SBML
            except Exception, e:
                print e
                print '\nWindows sbml load error'
                self.SBML = None
        else:
            try:
                import libsbml as SBML
                self.SBML = SBML
            except Exception, e:
                print e
                print 'Posix sbml load error'
                self.SBML = None
        assert self.SBML != None, '\nNo SBML library available'
                
    def getSbmlStringFromDisk(self, sbml, Dir=None):
        if sbml[-4:] != '.xml':
            print "Assuming .xml extension"
            sbml += '.xml'
        if Dir == None:
            Dir = CurrentDirectory
        assert os.path.exists(os.path.join(Dir, sbml)), \
            '\nFile %s does not exist' % os.path.join(Dir, sbml)
        self.sbml_file = os.path.join(Dir, sbml)
        sbmlF = file(self.sbml_file, 'r')
        self.sbml_string = sbmlF.read()
        sbmlF.close()
    
    def getSbmlStringFromString(self, sbml_string):
        self.sbml_string = sbml_string
        self.sbml_file = os.path.join(CurrentDirectory, 'sbml_string_loader.xml')

    def getSbmlModel(self, document=None):
        r = self.SBML.SBMLReader()
        if document == None:
            self.document = r.readSBMLFromString(self.sbml_string)
        else:
            self.document = document
            self.sbml_string = self.document.toSBML()
            self.sbml_file = os.path.join(CurrentDirectory, 'sbml_string_loader.xml')
        self.model = self.document.getModel()

    def getParsedModel(self):
        init_fixed = {}
        init_var = {}
        init_par = {}
        compartments = {}
        a_rules = {}
        
        for i in self.model.getListOfSpecies():
            parName = i.getId()
            if i.getBoundaryCondition() or i.getConstant():
                if i.getConstant() and not i.getBoundaryCondition():
                    print parName, ' is set as constant, assuming: BoundaryCondition = True'
                init_fixed.setdefault(parName, float(i.getInitialConcentration()))
            else:
                init_var.setdefault(parName, float(i.getInitialConcentration()))

        reactions = self.model.getListOfReactions()

        NetworkDict = dict([(i, dict.fromkeys(['Params',
                              'RateEq',
                              'Reagents',
                              'Modifiers',
                              'Type'])) for i in [j.getId() for j in reactions]])

        #print 'init_fixed\n', init_fixed 
        #print 'init_var\n', init_var 
        #print '\nNetwrokDict\n%s\n' % NetworkDict 
        
        for i in reactions:
            rDict = NetworkDict[i.getId()]
            j = i.getKineticLaw()
            par = []
            
            try:
                req = j.getFormula()
                p_names = None
                #if there are local parameters hash them to R_P
                if len(j.getListOfParameters()) > 0:
                    InfixParser.setNameStr('@', '@')
                    InfixParser.parse(req)
                    p_names = InfixParser.names
                    req = InfixParser.output
                    print req
                else:
                    InfixParser.setNameStr('', '')
                    InfixParser.parse(req)
                    p_names = InfixParser.names
                    req = InfixParser.output
                #print p_names
                #print req
                for k in j.getListOfParameters():
                    #TODO: replace with a regular expression based strategy
                    req = req.replace('@'+k.getId()+'@', i.getId() + '_' + k.getId())
                    par.append(i.getId() + '_' + k.getId())
                    init_par.setdefault(i.getId() + '_' + k.getId(), k.getValue())
                    # local parameters are hashed with reaction name
                    print 'Setting local parameter:', i.getId() + '_' + k.getId()
                    ##  # the working way
                    ##  par.append(k.getId())
                    ##  init_par.setdefault(k.getId(), k.getValue())
                rDict['Params'] = par
                req = req.replace('@','')
                rDict['RateEq'] = req
            except Exception, err:
                rDict['Params'] = []
                rDict['RateEq'] = ''
                print err

            Substrates = []
            Products = []

            for k in i.getListOfReactants():
                species = k.getSpecies()
                stoich = -k.getStoichiometry()
                Substrates.append((species,stoich))

            for k in i.getListOfProducts():
                species = k.getSpecies()
                stoich = k.getStoichiometry()
                Products.append((species,stoich))

            # this is to eliminate zero stoichiometries {0}xyz
            badList = []
            for sub in Substrates:
                if sub[1] == 0:
                    badList.append(sub)
            for bad in badList:
                Substrates.pop(Substrates.index(bad))

            badList = []
            for prod in Products:
                if prod[1] == 0:
                    badList.append(prod)
            for bad in badList:
                Products.pop(Products.index(bad))

            # this only makes sense when going to an input file (i think)
            ##  # add source/sink pools to nasty substrate/productless reactions - brett 20050908
            ##  if len(Substrates) == 0:
                ##  Substrates.append(('$pool',-1.0))
            ##  if len(Products) == 0:
                ##  Products.append(('$pool',1.0))

            ##  # print Substrates
            ##  # print Products

            rDict['Reagents'] = dict(Substrates+Products)
            if i.getReversible() == True:
                t = 'Rever'
            else:
                t = 'Irrev'
            rDict['Type'] = t
            NetworkDict[i.getId()].update(rDict)
            
            mods = []
            if len(i.getListOfModifiers()) > 0:
                for m in i.getListOfModifiers():
                    msbml = m.toSBML().split('\"')[1]
                    mods.append(msbml)
            else:
                mods = []
            rDict['Modifiers'] = mods
            #print "Modifiers", rDict['Modifiers']

        #print '\ninit_par\n', init_par  
        #print '\nNetwrokDict\n%s\n' % NetworkDict   

        # Add global parameters
        for x in self.model.getListOfParameters():
            if len(self.model.getListOfParameters()) > 0:
                init_par.setdefault(x.getId(), float(x.getValue()))

        #print '\ninit_par\n', init_par  


        # Initialise compartment volumes as a parameter - brett 20050908
        for comp in self.model.getListOfCompartments():
            compartments.setdefault(comp.getId(), float(comp.getVolume()))

        if len(compartments) > 1:
            print '\nINFO: PySCeS models are currently assumed to have a single compartment'

        if len(compartments) > 0:
            for comp in compartments.keys():
                init_par.setdefault(comp, compartments[comp])

        #print '\ninit_par\n', init_par  
        #print '\ncompartments\n', compartments  
     
        # Add assignment rules as forcing functions - brett 20050908
        for rule in self.model.getListOfRules():
            a_rules.setdefault(rule.getVariable(), rule.getFormula())

        #print '\na_rules\n', a_rules  

        notes = ''
        if self.model.getNotes() is not None:
            print 'Disabled for libsbml3'
            ## notes = self.model.getNotes().replace('\n','\n# ')

        #print "Building core2 attributes\n"
        #print "init_fixed\n", init_fixed
        #print "init_var\n", init_var
        #print "init_par\n", init_par
        #print "compartments\n", compartments
        #print "a_rules\n", a_rules

        # from _PysMod__NetworkDict
        self.ModelFile = self.model.getId()
        if len(self.ModelFile) == 0:
            self.ModelFile = self.model.getName()
            
        InitStrings = []
        for s in init_var.keys():
            line = 'self.' + s + '=' + str(init_var[s])
            InitStrings.append(line)
        for s in init_fixed.keys():
            line = 'self.' + s + '=' + str(init_fixed[s])
            InitStrings.append(line)
        for s in init_par.keys():
            line = 'self.' + s + '=' + str(init_par[s])
            InitStrings.append(line)
            
        self._PysMod__InitStrings = [s.replace('self.','') for s in InitStrings]
        #print 'self._PysMod__InitStrings\n', self._PysMod__InitStrings
        for s in InitStrings:
            exec(s)
        self.InitParams = init_par.keys()
        #print 'self.InitParams\n', self.InitParams
        self.fixed_species = init_fixed.keys()
        #print 'self.fixed_species\n', self.fixed_species
        self.species = init_var.keys()
        #print 'self.species\n', self.species
        self.parameters = init_par.keys()
        #print 'self.parameters\n', self.parameters
        self.reactions = NetworkDict.keys()
        #print 'self.reactions\n', self.reactions
        self.modifiers = []
        for r in NetworkDict.keys():
            self.modifiers.append((r, NetworkDict[r]['Modifiers']))
        #print 'self.modifiers\n', self.modifiers
        if len(a_rules.keys()) > 0:
            self._Function_forced = ''
            for r in a_rules.keys():
                self._Function_forced += r + ' = ' + a_rules[r] + '\n'
        else:
            self._Function_forced = 'pass\n'
        #print 'self._Function_forced\n', self._Function_forced
        self._Function_time = ''
        self._Function_user = ''
        self._Function_init = ''
        self._PysMod__NetworkDict = NetworkDict
        #print 'self._PysMod__NetworkDict\n', self._PysMod__NetworkDict


if __name__ == '__main__':
    from PyscesCore2 import NewCore
    print "\n*****\nTESTING PSC2CORE\n*****\n"
    
    c1 = PscToCore()
    c1.getPSCFileFromDisk('test_bridge.psc', ModelDir='c:\\mypysces\\pscmodels', WorkDir='c:\\temp')
    c1.getParsedModel()
    
    bmap = NewCore(c1)

    print 'Reactions\n', bmap.hasReactions()
    print 'Species\n', bmap.hasSpecies()
    print 'FixedSpecies\n', bmap.hasFixedSpecies()
    print ' '
    print 'R1 has reagents\n', bmap.R1.hasReagents()
    print 'R1 has sub\n', bmap.R1.hasSubstrates()
    print 'R1 has prod\n', bmap.R1.hasProducts()
    print 'R1 has mod\n', bmap.R1.hasModifiers()
    print ' '
    print 's2 is reagent\n', bmap.s2.isReagentOf()
    print 's2 is sub\n', bmap.s2.isSubstrateOf()
    print 's2 is prod\n', bmap.s2.isProductOf()
    print 's2 is mod\n', bmap.s2.isModifierOf()
    print ' '
    print 'R2 stoich\n', bmap.R2.stoichiometry
    print ' '
    print 'findReactionsThatIncludeAllSpecifiedReagents(A, B):', bmap.findReactionsThatIncludeAllSpecifiedReagents('s0','s1')

    print '\nParameter associations'
    for p in bmap.global_parameters:
        print '<\'%s\n%s.isParameterOf() %s' % (str(type(p)).split('.')[-1], p.name, p.isParameterOf())
    
    print '\nbmap Rates'
    for r in bmap.reactions:
        print r.name, '=', r.__call__()

    